import SimpleAnimateProperties from "./SimpleAnimateProperties";
import MathUtils, { EaseType } from "../MathUtils";
import CommonUtils from "../CommonUtils";
import AudioManager from "../AudioManager";
import App from "../framework/App";

/**
 * 简单动画工具，用于快速对目标进行抖动、摇晃、闪烁、飞行等效果
 */
export default class SimpleAnimate {

    /**
     * 抖动目标
     * position:表示抖动在对象顶部、底部，左侧还是右侧
     */
    static shake(delay: number, target: cc.Node, duration: number, offset: number = 3, onFinish?: Function, thisObj?: any) {
        let interval = 0.05;
        let pro = target.getComponent(SimpleAnimateProperties);
        if (!pro) {
            pro = target.addComponent(SimpleAnimateProperties);
        }
        pro.defaultPos = target.position;
        pro.times = Math.floor(duration / interval);
        pro.n = 0;
        pro.schedule(() => {
            target.setPosition(pro.defaultPos.x + (Math.random() * 2 - 1) * offset, pro.defaultPos.y + (Math.random() * 2 - 1) * offset);
            pro.n++;
            if (pro.n > pro.times) {
                target.setPosition(pro.defaultPos);
                if (onFinish) {
                    onFinish.call(thisObj);
                }
            }
        }, interval, pro.times, delay);
    }

    /**
     * 弹一下(耸肩膀)
     */
    static shrug(delay: number, target: cc.Node, scale: number = 0.95, duration: number = 0.1, onFinish?: Function) {
        let orginScaleX = target.scaleX;
        let orginScaleY = target.scaleX;
        cc.tween(target).delay(delay).to(duration, { scaleX: orginScaleX * (1 + 1 - scale), scaleY: orginScaleY * scale })
            .to(duration, { scaleX: orginScaleX, scaleY: orginScaleY }, { easing: 'backOut' }).call(() => {
                if (onFinish) {
                    onFinish();
                }
            }).start();
    }

    /**
     * 摇晃
     * @param zoom 是否同时放大，默认true
     */
    static sway(delay: number, target: cc.Node, times: number = 2, angle: number = 5, zoom: boolean = true, onFinish?: Function) {
        let pro = target.getComponent(SimpleAnimateProperties);
        if (!pro) {
            pro = target.addComponent(SimpleAnimateProperties);
        }
        let defaultAngle = target.angle;
        let defaultScaleX = target.scaleX;
        let defaultScaleY = target.scaleY;
        let n: number = 0;
        pro.schedule(() => {
            cc.tween(target).to(0.05, { angle: defaultAngle - angle }).call(() => {
                cc.tween(target).to(0.1, { angle: defaultAngle + angle }).call(() => {
                    if (n < times - 1) {
                        cc.tween(target).to(0.05, { angle: defaultAngle }).start();
                    } else {
                        cc.tween(target).to(0.5, { angle: defaultAngle }, { easing: 'elasticOut' }).call(() => {
                            if (onFinish) {
                                onFinish();
                            }
                        }).start();
                        if (zoom) {
                            cc.tween(target).to(0.15, { scaleX: defaultScaleX, scaleY: defaultScaleY }).start();
                        }
                    }
                    n += 1;
                }).start();
            }).start();
            if (zoom) {
                cc.tween(target).to(0.1, { scaleX: defaultScaleX * 1.2, scaleY: defaultScaleY * 1.2 }).start();
            }
        }, 0.2, times - 1, delay);
    }

    /**
     * 下落(触地压缩效果)
     */
    static drop(delay: number, target: cc.Node, duration: number, toY: number, arrivedCallback?: Function) {
        let originScaleX = target.scaleX;
        let originScaleY = target.scaleY;
        let originAnchorY = target.anchorY;
        let offsetY = (0 - target.anchorY) * target.height;
        target.anchorY = 0;
        target.y += offsetY;
        toY += offsetY;
        cc.tween(target).delay(delay).to(duration, { y: toY }).call(() => {
            if (arrivedCallback) {
                arrivedCallback();
            }
            cc.tween(target).to(0.05, { scaleX: originScaleX * 1.2, scaleY: originScaleY * 0.8 }).call(() => {
                cc.tween(target).to(0.2, { scaleX: originScaleX, scaleY: originScaleY }, { easing: 'backOut' }).call(() => {
                    target.anchorY = originAnchorY;
                    target.y -= offsetY;
                }).start();
            }).start();
        }).start();
    }

    /**
     * 群飞特效(金币、爱心、积分等)
     * @param pathOffset 飞行过程种的偏移量，默认0
     * @param minScale 随机的最小Scale,默认1
     * @param maxScale 随机的最大Scale,默认1
     * @param autoDestroy 默认true
     * @param arrivedCallback 第一个元素飞到目标点时触发一次
     */
    static fly(prefabPath: string, fromPos: cc.Vec2, fromPosOffset: cc.Vec2, toPos: cc.Vec2, toPosOffset: cc.Vec2, pathOffset: number = 0, amount: number, minScale: number = 1, maxScale: number = 1, autoDestroy = true, arrivedCallback?: Function) {
        let dis = fromPos.sub(toPos).mag();
        let node = new cc.Node('fly');
        let emptyNodePro = node.addComponent(SimpleAnimateProperties);
        emptyNodePro.destroyOnStageSwap(true);
        let maxDuration = 0.5;
        emptyNodePro.scheduleOnce(() => {
            //空对象用于配发，喷发完成后移除自己，不影响已喷发出的对象
            emptyNodePro.node.destroy();
        }, amount * maxDuration);
        let prefab = App.instance.resPreloader.getRes(prefabPath) as cc.Prefab;
        for (let i = 0; i < amount; i++) {
            let delay = 0;//确保第0个元素先飞到以便第一时间触发回调
            if (i > 0) {
                delay = Math.random() * maxDuration;
            }
            emptyNodePro.scheduleOnce(() => {
                let target = new cc.Node();
                let particle = cc.instantiate(prefab);
                target.addChild(particle);
                particle.position = cc.Vec2.ZERO;
                //添加组件，以便监听当Stage切换时移除
                target.addComponent(SimpleAnimateProperties);
                cc.Canvas.instance.node.addChild(target);
                target.scale = CommonUtils.randomFloat(minScale, maxScale);
                let _fromPos = fromPos;
                if (fromPosOffset != cc.Vec2.ZERO) {
                    _fromPos.x += (Math.random() * 2 - 1) * fromPosOffset.x;
                    _fromPos.y += (Math.random() * 2 - 1) * fromPosOffset.y;
                }
                target.setPosition(_fromPos);
                let _toPos = toPos;
                if (toPosOffset != cc.Vec2.ZERO) {
                    _toPos.x += (Math.random() * 2 - 1) * toPosOffset.x;
                    _toPos.y += (Math.random() * 2 - 1) * toPosOffset.y;
                }
                let duration = dis * CommonUtils.randomFloat(0.0005, 0.0009);
                cc.tween(target).to(duration, { position: _toPos }).call(() => {
                    if (arrivedCallback && i == 0) {
                        arrivedCallback(particle);
                    }
                    if (autoDestroy) {
                        target.destroy();
                    }
                }).start();
                let particleOffset: cc.Vec2 = cc.v2(CommonUtils.randomFloat(-pathOffset, pathOffset), CommonUtils.randomFloat(-pathOffset, pathOffset));
                cc.tween(particle).to(duration * 0.5, { position: particleOffset }).call(() => {
                    cc.tween(particle).to(duration * 0.5, { position: cc.Vec2.ZERO }).start();
                }).start();
            }, delay);
        }
    }

    /**
     * 缩放提示
     * @param scaleRatio 默认1.2
     */
    static zoomTip(delay: number, target: cc.Node, times: number, scaleRatio = 1.2, finishCallback?: Function) {
        let pro = target.getComponent(SimpleAnimateProperties);
        if (!pro) {
            pro = target.addComponent(SimpleAnimateProperties);
            pro.defaultScale.x = target.scaleX;
            pro.defaultScale.y = target.scaleY;
        }
        pro.n = times;
        pro.callback = finishCallback;
        pro.zoomTipOnce(delay, scaleRatio);
    }

    /**
     * 以贝塞尔曲线移动
     * @param target 移动对象
     * @param from 起始点
     * @param middle 中间点
     * @param to 结束点
     * @param stepNumber 步数
     * @param updateInterval 刷新时间间隔
     * @param finishCallback 结束回调
     */
    static moveByBezier(delay: number, target: cc.Node, from: cc.Vec2, middle: cc.Vec2, to: cc.Vec2, stepNumber: number, easeType: EaseType, finishCallback?: Function) {
        if (!target) {
            console.log('target为null');
            return;
        }
        let pro = target.getComponent(SimpleAnimateProperties);
        if (!pro) {
            pro = target.addComponent(SimpleAnimateProperties);
        }
        pro.n = 0;
        pro.positions = MathUtils.getBezierPoints(from, middle, to, stepNumber, easeType);
        pro.callback = finishCallback;
        pro.stepNumber = stepNumber;
        pro.startMoveByBezier(delay);
    }

    /**
     * 放置于平台的物体移动到某处，开始和结束附带预备和缓冲效果
     * @param delay 延迟开始
     * @param target 移动目标
     * @param duration 时间
     * @param toPos 目标位置
     * @param endAnchor 倾斜锚点
     * @param endSkew 倾斜量
     * @param finishCallback 结束回调
     */
    static moveBySkew(delay: number, target: cc.Node, duration: number, toX: number, onMoveFinish?: Function, onSkewFinish?: Function) {
        let skew = target.x < toX ? 4 : -4;
        let endAnchor = cc.v2(0.5, 0);
        let originAnchorX = target.anchorX;
        let originAnchorY = target.anchorY;
        let offsetX = (endAnchor.x - target.anchorX) * target.width;
        let offsetY = (endAnchor.y - target.anchorY) * target.height;
        target.anchorX = endAnchor.x;
        target.anchorY = endAnchor.y;
        target.x += offsetX * target.scaleX;
        target.y += offsetY * target.scaleY;
        toX += offsetX;
        cc.tween(target).delay(delay).to(duration, { x: toX }, { easing: 'sineOut' }).call(() => {
            if (onMoveFinish) {
                onMoveFinish();
            }
            cc.tween(target).to(0.1, { skewX: skew }, { easing: 'sineOut' }).to(0.1, { skewX: 0 }, { easing: 'sineOut' }).call(() => {
                target.anchorX = originAnchorX;
                target.anchorY = originAnchorY;
                target.x -= offsetX * target.scaleX;
                target.y -= offsetY * target.scaleY;
                if (onSkewFinish) {
                    onSkewFinish();
                }
            }).start();
        }).start();
    }

    /**
     * 跳跃到目标位置
     * @param stepNumber
     */
    static jump(delay: number, target: cc.Node, toPos: cc.Vec2, height: number, stepNumber: number, onFinish: Function) {
        let scaleX = target.scaleX;
        let scaleY = target.scaleY;
        cc.tween(target).delay(delay).to(0.1, { scaleX: scaleX * 1.2, scaleY: scaleY * 0.8 }, { easing: 'sineOut' }).call(() => {
            target.scaleX = scaleX * 0.8;
            target.scaleY = scaleY * 1.2;
            let middle = cc.v2((target.x + toPos.x) * 0.5, Math.max(target.y, toPos.y) + height);
            SimpleAnimate.moveByBezier(0, target, target.position, middle, toPos, stepNumber, EaseType.None, () => {
                AudioManager.playFX('common/fx_Jump')
                cc.tween(target).delay(delay).to(0.1, { scaleX: scaleX * 1.2, scaleY: scaleY * 0.8 }, { easing: 'sineOut' }).call(() => {
                    cc.tween(target).delay(delay).to(0.1, { scaleX: scaleX, scaleY: scaleY }, { easing: 'sineOut' }).call(() => {
                        if (onFinish) {
                            onFinish();
                        }
                    }).start();
                }).start();
            });
        }).start();
    }

}